/*
 *  MicroEmulator
 *  Copyright (C) 2001,2002 Bartek Teodorczyk <barteo@barteo.net>
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2.1 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *  
 *  @version $Id: SwingDeviceComponent.java 1446 2007-10-24 16:30:17Z barteo $ 
 */

package org.microemu.app.ui.swing;

import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.KeyboardFocusManager;
import java.awt.Polygon;
import java.awt.Shape;
import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.StringSelection;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Timer;
import java.util.TimerTask;

import javax.microedition.lcdui.Command;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.microedition.lcdui.CommandManager;

import org.microemu.DisplayAccess;
import org.microemu.DisplayComponent;
import org.microemu.MIDletAccess;
import org.microemu.MIDletBridge;
import org.microemu.app.Common;
import org.microemu.device.Device;
import org.microemu.device.DeviceFactory;
import org.microemu.device.impl.DeviceDisplayImpl;
import org.microemu.device.impl.Rectangle;
import org.microemu.device.impl.SoftButton;
import org.microemu.device.j2se.J2SEButton;
import org.microemu.device.j2se.J2SEDeviceButtonsHelper;
import org.microemu.device.j2se.J2SEDeviceDisplay;
import org.microemu.device.j2se.J2SEImmutableImage;
import org.microemu.device.j2se.J2SEInputMethod;
import org.microemu.device.j2se.J2SEMutableImage;
import org.microemu.log.Logger;

/**
 * This is a child {@link Component} of {@link SwingDeviceComponent}.
 * 
 * This is responsible for rendering the actual device and display screen.
 */
class SwingDeviceComponentConcreteView extends JPanel
                                        implements KeyListener, FocusListener
{
	private static final long serialVersionUID = 1L;
	
	private SwingDeviceComponent iParentComponent;
	
	private SwingDisplayComponent dc;

	private J2SEButton prevOverButton;

	private J2SEButton overButton;

	private J2SEButton pressedButton;

	private boolean mouseButtonDown = false;

	/**
	 * BlueWhaleSystems fix: Ramesh Nair - 23 Jan 2008
	 * 
	 * A number of changes to allow for users to press <ALT> + <3 numbers> to 
	 * enter any ASCII character in range 0 - 255. This stores entered numbers in 
	 * the lastFewKeysPressedWhilstHoldingDownControlKeys array until the <ALT> 
	 * key is released, at which point the character is computed and shown.
	 */  	
	private ArrayList lastFewKeysPressedWhilstHoldingDownControlKeys = 
	                        new ArrayList();
	
    /**
     * BlueWhaleSystems fix: Ramesh Nair - 23 Jan 2008
     * 
     * On certain keyboards (e.g. Italian) you need to press <CTRL> + <ALT> + 
     * <some key> in order to enter certain characters, e.g. '@' character. 
     * We set this flag to true whenever we want keyTyped() to ensure that 
     * special characters get output.
     */     	
	private boolean lastKeyPressedCallbackDidNotProcessCharacterCode = false;

	
    /**
     * BlueWhaleSystems fix: Ramesh Nair - 13 May 2008
     * 
     * Keep track of SHIFT key state.
     */         	
	private boolean shiftKeyIsDown = false;
	
	
	/**
	 * Offscreen image for double buffering purposes.
	 */
	private Image offi;

	/**
	 * {@link Graphics} object for {@link #offi}.
	 */
	private Graphics offg;

	private boolean showMouseCoordinates = false;

	private int pressedX;

	private int pressedY;

	private static class MouseRepeatedTimerTask extends TimerTask {

		private static final int DELAY = 100;

		Timer timer;

		Component source;

		J2SEButton button;

		J2SEInputMethod inputMethod;

		static MouseRepeatedTimerTask task;

		static void schedule(Component source, J2SEButton button, J2SEInputMethod inputMethod) {
			if (task != null) {
				task.cancel();
			}
			task = new MouseRepeatedTimerTask();
			task.source = source;
			task.button = button;
			task.inputMethod = inputMethod;
			task.timer = new Timer();
			task.timer.scheduleAtFixedRate(task, 5 * DELAY, DELAY);
		}

		static void stop() {
			if (task != null) {
				task.inputMethod = null;
				if (task.timer != null) {
					task.timer.cancel();
				}
				task.cancel();
				task = null;
			}
		}

		public static void mouseReleased() {
			if ((task != null) && (task.inputMethod != null)) {
				task.inputMethod.buttonReleased(task.button, '\0', false);
				stop();
			}

		}

		public void run() {
			if (inputMethod != null) {
				inputMethod.buttonPressed(button, '\0', false);
			}
		}

	}


	
	private MouseAdapter mouseListener = new MouseAdapter() {

	    /**
	     * BlueWhaleSystems fix: Ramesh Nair - 3 Jan 2008
	     *
	     * Now, as soon as the mouse enters the skin area the midlet receives 
	     * input focus.
	     */
	    public void mouseEntered(MouseEvent e)
	    {
	        SwingDeviceComponentConcreteView.this.requestFocusInWindow();
	    }
	    
		public void mousePressed(MouseEvent e) {
            //SwingDeviceComponentConcreteView.this.requestFocusInWindow();
		    
			mouseButtonDown = true;
			pressedX = e.getX();
			pressedY = e.getY();

			MouseRepeatedTimerTask.stop();
			if (MIDletBridge.getCurrentMIDlet() == null) {
				return;
			}

			Device device = DeviceFactory.getDevice();
			J2SEInputMethod inputMethod = (J2SEInputMethod) device.getInputMethod();
			// if the displayable is in full screen mode, we should not
			// invoke any associated commands, but send the raw key codes
			// instead
			boolean fullScreenMode = device.getDeviceDisplay().isFullScreenMode();

			pressedButton = J2SEDeviceButtonsHelper.getSkinButton(e);
			if (pressedButton != null) {
				if (pressedButton instanceof SoftButton && !fullScreenMode) {
					Command cmd = ((SoftButton) pressedButton).getCommand();
					if (cmd != null) {
						MIDletAccess ma = MIDletBridge.getMIDletAccess();
						if (ma == null) {
							return;
						}
						DisplayAccess da = ma.getDisplayAccess();
						if (da == null) {
							return;
						}
						da.commandAction(cmd, da.getCurrent());
						// This was what was here before: CommandManager.getInstance().commandAction(cmd);
					}
				} else {
					inputMethod.buttonPressed(pressedButton, '\0', false);
					MouseRepeatedTimerTask.schedule(SwingDeviceComponentConcreteView.this, pressedButton, inputMethod);
				}
				// optimize for some video cards.
				repaint(pressedButton.getShape().getBounds());
			}
			
		}

		public void mouseReleased(MouseEvent e) {
			mouseButtonDown = false;
			MouseRepeatedTimerTask.stop();

			if (pressedButton == null) {
				return;
			}

			if (MIDletBridge.getCurrentMIDlet() == null) {
				return;
			}

			Device device = DeviceFactory.getDevice();
			J2SEInputMethod inputMethod = (J2SEInputMethod) device.getInputMethod();
			J2SEButton prevOverButton = J2SEDeviceButtonsHelper.getSkinButton(e);
			if (prevOverButton != null) {
				inputMethod.buttonReleased(prevOverButton, '\0', false);
			}
			pressedButton = null;
			// optimize for some video cards.
			if (prevOverButton != null) {
				repaint(prevOverButton.getShape().getBounds());
			} else {
				repaint();
			}
		}

	};

	private MouseMotionListener mouseMotionListener = new MouseMotionListener() {

		public void mouseDragged(MouseEvent e) {
			mouseMoved(e);
		}

		public void mouseMoved(MouseEvent e) {
		    
            SwingDeviceComponentConcreteView.this.requestFocusInWindow();
		    
			if (showMouseCoordinates) {
				StringBuffer buf = new StringBuffer();
				if (mouseButtonDown) {
					int width = e.getX() - pressedX;
					int height = e.getY() - pressedY;
					buf.append(pressedX).append(",").append(pressedY).append(" ").append(width).append("x").append(
							height);
				} else {
					buf.append(e.getX()).append(",").append(e.getY());
				}
				Common.setStatusBar(buf.toString());
			}

			if (mouseButtonDown && pressedButton == null) {
				return;
			}

			prevOverButton = overButton;
			overButton = J2SEDeviceButtonsHelper.getSkinButton(e);
			if (overButton != prevOverButton) {
				// optimize for some video cards.
				if (prevOverButton != null) {
					MouseRepeatedTimerTask.mouseReleased();
					pressedButton = null;
					repaint(prevOverButton.getShape().getBounds());
				}
				if (overButton != null) {
					repaint(overButton.getShape().getBounds());
				}
			} else if (overButton == null) {
				MouseRepeatedTimerTask.mouseReleased();
				pressedButton = null;
				if (prevOverButton != null) {
					repaint(prevOverButton.getShape().getBounds());
				}
			}
		}

	};

	
	public static SwingDeviceComponentConcreteView 
	        getInstance(SwingDeviceComponent parentComponent)
	{
	    return new SwingDeviceComponentConcreteView(parentComponent);
	}
	
	
	
	private SwingDeviceComponentConcreteView(
	        SwingDeviceComponent parentComponent)
	{
		/**
		 * BlueWhaleSystems fix: Ramesh Nair - 3 Jan 2008
		 * 
		 * Now, as soon as the mouse enters the device skin area the midlet receives input focus.
		 */
		iParentComponent = parentComponent;
	    
		dc = new SwingDisplayComponent(iParentComponent);
		setLayout(new XYLayout());

		addMouseListener(mouseListener);
		addMouseMotionListener(mouseMotionListener);
        addFocusListener( this );		
        addKeyListener( this );
	}
	

	public DisplayComponent getDisplayComponent() {
		return dc;
	}

	public void init() {
		dc.init();

		remove(dc);

		Rectangle r = ((J2SEDeviceDisplay) DeviceFactory.getDevice().getDeviceDisplay()).getDisplayRectangle();
		add(dc, new XYConstraints(r.x, r.y, -1, -1));

		revalidate();
	}

	private void repaint(Rectangle r) {
		repaint(r.x, r.y, r.width, r.height);
	}

	public void switchShowMouseCoordinates() {
		// TODO skin editing mode.
		// showMouseCoordinates = !showMouseCoordinates;
		dc.switchShowMouseCoordinates();
	}

	
	public MouseListener getMouseListener()
	{
	    return mouseListener;
	}
	
	public MouseMotionListener getMouseMotionListener()
	{
	    return mouseMotionListener;
	}
	
	
	
	public void keyTyped(KeyEvent ev) {
	    /**
	     * BlueWhaleSystems fix: Ramesh Nair - 12 May 2008
	     * 
	     * We've made some changes to input even handlers. This check is here 
	     * just in case an event gets called after it has already been handled.
	     */	    
	    if (ev.isConsumed())
	        return;
	    ev.consume();
	    
		if (MIDletBridge.getCurrentMIDlet() == null) {
			return;
		}

        J2SEInputMethod inputMethod = ((J2SEInputMethod) DeviceFactory.getDevice().getInputMethod());

//System.out.println("typed -> code: " + ev.getKeyCode() + ", mod: " + ev.getModifiers() + ", char: " + ev.getKeyChar());        
        
        /**
         * BlueWhaleSystems fix: Ramesh Nair - 23 Jan 2008
         * 
         * On certain keyboards (e.g. Italian) you need to press <CTRL> + <ALT> + 
         * <some key> in order to enter certain characters, e.g. '@' character. This 
         * change will pick up on those entries. 
         */
        // if character wasn't processed by keyPressed()
        if (lastKeyPressedCallbackDidNotProcessCharacterCode)
		{
		    // then this might be an international keyboard. just show the 
		    // character typed
            inputMethod.pasteAtCursor( String.valueOf(ev.getKeyChar()) );		    
		}
		
        J2SEButton button = inputMethod.getButton(ev);		
		if (button != null) {
			inputMethod.buttonTyped(button);
		}
	}

	public void keyPressed(KeyEvent ev) {
	    
        /**
         * BlueWhaleSystems fix: Ramesh Nair - 12 May 2008
         * 
         * We've made some changes to input even handlers. This check is here 
         * just in case an event gets called after it has already been handled.
         */     	    
        if (ev.isConsumed())
            return;
        ev.consume();
	    
	    // reset flag
	    lastKeyPressedCallbackDidNotProcessCharacterCode = false;
	    
		if (MIDletBridge.getCurrentMIDlet() == null) {
			return;
		}

//System.out.println("pressed -> code: " + ev.getKeyCode() + ", mod: " + ev.getModifiers() + ", char: " + ev.getKeyChar());        
		
		Device device = DeviceFactory.getDevice();
		J2SEInputMethod inputMethod = (J2SEInputMethod) device.getInputMethod();

		boolean currentlyEnteringACharacterCode = false;

        int keyCode = ev.getKeyCode();
		char keyChar = '\0';
		if (ev.getKeyChar() >= 32 && ev.getKeyChar() != 65535) {
			keyChar = ev.getKeyChar();
		}

		if (ev.isControlDown()) 
		{
		    // CTRL + V (paste from clipboard)
		    if (KeyEvent.VK_V == keyCode)
		    {
	            Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
	            Transferable transferable = clipboard.getContents(null);
	            if (transferable != null) {
	                try {
	                    Object data = transferable.getTransferData(DataFlavor.stringFlavor);
	                    if (data instanceof String) {
	                        inputMethod.pasteAtCursor((String) data);
	                    }
	                } catch (UnsupportedFlavorException ex) {
	                    Logger.error(ex);
	                } catch (IOException ex) {
	                    Logger.error(ex);
	                }
	            }
	            return;
		    } 
		    /**
		     * CTRL + C (copy to clipboard) or CTRL + V (cut to clipboard)
		     * BlueWhaleSystems fix: Ramesh Nair - 14 May 2008
		     */
		    else if (KeyEvent.VK_C == keyCode || KeyEvent.VK_X == keyCode) 
            {
                String highlightedText = inputMethod.getHighlightedText();
                if (null != highlightedText)
                {
                    StringSelection str = new StringSelection(highlightedText);
                    try
                    {
                        Toolkit.getDefaultToolkit().getSystemClipboard().setContents( str, str );
                    }
                    catch (IllegalStateException e)
                    {
                        Logger.error( "Error copying to clipboard", e );
                    }
                }
                
                if (KeyEvent.VK_X == keyCode)
                {
                    inputMethod.deleteHighlightedText();
                }
                
                return;
            } 
            /**
             * CTRL + A (select all text)
             * BlueWhaleSystems fix: Ramesh Nair - 19 May 2008
             */
            else if (KeyEvent.VK_A == keyCode) 
            {
                inputMethod.highlightAllText();
                return;
            } 
		    // CTRL + ALT
		    else if (ev.isAltDown())
		    {
                // Do nothing as it will be handled in keyTyped()
		        lastKeyPressedCallbackDidNotProcessCharacterCode = true;
		        return;
		    }
		} 
		// ALT key held down
		else if (ev.isAltDown())
		{
            // if we've pressed a number key then we might be entering a 
		    // character code
			/* 
			 * BlueWhaleSystems fix: Michael Busheikin - 3 Apr 2009
			 * When ALT was down it was incorrectly comparing the keyChar to VK_9.
			 */
            if (KeyEvent.VK_0 <= keyCode && KeyEvent.VK_9 >= keyCode)
            {
                currentlyEnteringACharacterCode = true;
                lastFewKeysPressedWhilstHoldingDownControlKeys.add( 
                        new Integer((int)keyChar - KeyEvent.VK_0) );
                return;
            }
		}
		
		if (!currentlyEnteringACharacterCode)
		{
		    // not currently entering a character code
		    lastFewKeysPressedWhilstHoldingDownControlKeys.clear();
		}
		

		// if the control keys are pressed on their own then do nothing.
		switch (keyCode) {
		    case KeyEvent.VK_SHIFT:
		        shiftKeyIsDown = true;
		        break;
		    case KeyEvent.VK_ALT:
    		case KeyEvent.VK_CONTROL:
    			return;
    		case 0:
    			// Don't know what is the case was intended for but this may be
    			// national keyboard letter, so let it work
    			if (ev.getKeyChar() == '\0') {
    				return;
    			}
		}
		
		if (ev.isShiftDown())
		{
		    shiftKeyIsDown = true;
		}
		

		J2SEButton button = inputMethod.getButton(ev);
		if (button != null) {
			pressedButton = button;
			// numeric keypad functions as hot keys for buttons only
			if ((KeyEvent.VK_NUMPAD0 <= keyCode) && (KeyEvent.VK_NUMPAD9 >= keyCode)) 
			{
				keyChar = '\0';
			}
			// soft buttons
			if ((ev.getKeyCode() >= KeyEvent.VK_F1) && (ev.getKeyCode() <= KeyEvent.VK_F12)) {
				keyChar = '\0';
			}

			inputMethod.buttonPressed(button, keyChar, shiftKeyIsDown);
			
			org.microemu.device.impl.Shape shape = button.getShape();
			if (shape != null) {
				repaint(shape.getBounds());
			}
		} else {
            lastKeyPressedCallbackDidNotProcessCharacterCode = true;
			// Logger.debug0x("no button for KeyCode", ev.getKeyCode());
		}
		/**
		 * BlueWhaleSystems fix: Michael Busheikin - 3 Apr 2009
		 * Setting the call to inputMethod back to how it was before the merge on 26 Mar, 2009.
		 * I had made a bad judgement call on duplicating this call outside the loop.
		 * See ticket:3080
		 */
		//inputMethod.buttonPressed(button, keyChar, shiftKeyIsDown);
	}

	
	
	public void keyReleased(KeyEvent ev) {
	    
        /**
         * BlueWhaleSystems fix: Ramesh Nair - 12 May 2008
         * 
         * We've made some changes to input event handlers. This check is here 
         * just in case an event gets called after it has already been handled.
         */     	    
        if (ev.isConsumed())
            return;
        ev.consume();
	    
		if (MIDletBridge.getCurrentMIDlet() == null) {
			return;
		}

//System.out.println("released -> code: " + ev.getKeyCode() + ", mod: " + ev.getModifiers() + ", char: " + ev.getKeyChar());        
		
		Device device = DeviceFactory.getDevice();
        J2SEInputMethod inputMethod = (J2SEInputMethod) device.getInputMethod();

		// if we're in the middle of entering a character code
        int sz = lastFewKeysPressedWhilstHoldingDownControlKeys.size();        
        if (0 < sz)
        {
            // if we've just finished entering the character code
            if (0 == ev.getModifiers())
            {
                // enter the character into the input field
                sz = Math.min( sz, 3 ); // max. 3 chars
                int characterCode = 0;
                for (int i=0; i<sz; ++i)
                {
                    characterCode +=
                        ((Integer)lastFewKeysPressedWhilstHoldingDownControlKeys.get( i ))
                        .intValue() * (int)(Math.pow( 10, sz-i-1 ));
                }
                
                inputMethod.pasteAtCursor( String.valueOf((char)characterCode) );
                lastFewKeysPressedWhilstHoldingDownControlKeys.clear();
                return;
            }
        }
		
		
		switch (ev.getKeyCode()) {
		    case KeyEvent.VK_SHIFT:
		        shiftKeyIsDown = false;
		        break;
    		case KeyEvent.VK_ALT:
    		case KeyEvent.VK_CONTROL:
    			return;
    		//TODO: Michael Busheikin, 24/03/09: Add case for keycode == 0, see Microemu source
		}
		
		if (!ev.isShiftDown())
		{
		    shiftKeyIsDown = false;
		}
		

		char keyChar = '\0';
		if (ev.getKeyChar() >= 32 && ev.getKeyChar() != 65535) {
			keyChar = ev.getKeyChar();
		}
		// numeric keypad functions as hot keys for buttons only
		if ((ev.getKeyCode() >= KeyEvent.VK_NUMPAD0) && (ev.getKeyCode() <= KeyEvent.VK_NUMPAD9)) {
			keyChar = '\0';
		}
		// soft buttons
		if ((ev.getKeyCode() >= KeyEvent.VK_F1) && (ev.getKeyCode() <= KeyEvent.VK_F12)) {
			keyChar = '\0';
		}
		
		J2SEButton button = inputMethod.getButton(ev);
		if (button != null) {
			inputMethod.buttonReleased(button, keyChar, shiftKeyIsDown);
		}

		prevOverButton = pressedButton;
		pressedButton = null;
		/*
		 * BlueWhaleSystems fix: Michael Busheikin - Apr 15, 2009
		 * 
		 * At this point the pressedButton isn't necessarily the button in the event ev. 
		 * Specifically, this happens when two (or more) keys are pressed at the same time,
		 * possible when using a real keyboard. This will ensure that every key is repainted to
		 * its normal state on a keyReleased. See ticket:3117 SetupUserSettings: Microemu painting 
		 * problems when buttons are pressed on phone models.
		 */
		if (button != null) {
			org.microemu.device.impl.Shape shape = button.getShape();
			if (shape != null) {
				repaint(shape.getBounds());
			}
		}
	}

	public MouseListener getDefaultMouseListener() {
		return mouseListener;
	}

	public MouseMotionListener getDefaultMouseMotionListener() {
		return mouseMotionListener;
	}


	protected void paintComponent(Graphics g) {
	    super.paintComponent( g );

		if (offg == null || offi.getWidth(null) != getSize().width || offi.getHeight(null) != getSize().height) {
			offi = new J2SEMutableImage(getSize().width, getSize().height).getImage();
			offg = offi.getGraphics();
		}

		Dimension size = getSize();
		
		offg.setColor(UIManager.getColor("text"));
		offg.fillRect(0, 0, size.width, size.height);
		Device device = DeviceFactory.getDevice();
		if (device == null) {
			g.drawRect(0, 0, getWidth() - 1, getHeight() - 1);
			return;
		}
	    if (((DeviceDisplayImpl) device.getDeviceDisplay()).isResizable()) {
	        return;
	    }

	    offg.drawImage(((J2SEImmutableImage) device.getNormalImage()).getImage(), 0, 0, this);

		if (prevOverButton != null) {
			org.microemu.device.impl.Shape shape = prevOverButton.getShape();
			if (shape != null) {
				drawImageInShape(offg, ((J2SEImmutableImage) device.getNormalImage()).getImage(), shape);
			}
			prevOverButton = null;
		}
		if (overButton != null) {
			org.microemu.device.impl.Shape shape = overButton.getShape();
			if (shape != null) {
				drawImageInShape(offg, ((J2SEImmutableImage) device.getOverImage()).getImage(), shape);
			}
		}
		if (pressedButton != null) {
			org.microemu.device.impl.Shape shape = pressedButton.getShape();
			if (shape != null) {
				drawImageInShape(offg, ((J2SEImmutableImage) device.getPressedImage()).getImage(), shape);
			}
		}

		g.drawImage(offi, 0, 0, null);
	}

	private void drawImageInShape(Graphics g, Image image, org.microemu.device.impl.Shape shape) {
		Shape clipSave = g.getClip();
		if (shape instanceof org.microemu.device.impl.Polygon) {
			Polygon poly = new Polygon(((org.microemu.device.impl.Polygon) shape).xpoints,
					((org.microemu.device.impl.Polygon) shape).ypoints,
					((org.microemu.device.impl.Polygon) shape).npoints);
			g.setClip(poly);
		}
		org.microemu.device.impl.Rectangle r = shape.getBounds();
		g.drawImage(image, r.x, r.y, r.x + r.width, r.y + r.height, r.x, r.y, r.x + r.width, r.y + r.height, null);
		g.setClip(clipSave);
	}

	public Dimension getPreferredSize() {
  		Device device = DeviceFactory.getDevice();
  		if (device == null) {
  			return new Dimension(0, 0);
  		}
  		
  		DeviceDisplayImpl deviceDisplay = (DeviceDisplayImpl) DeviceFactory.getDevice().getDeviceDisplay();
  		if (deviceDisplay.isResizable()) {
  		    return new Dimension(deviceDisplay.getFullWidth(), deviceDisplay.getFullHeight());
  		} else {  		
  		    javax.microedition.lcdui.Image img = device.getNormalImage();  		
  		    return new Dimension(img.getWidth(), img.getHeight());
  		}
	}



    /**
     * BlueWhaleSystems fix: Ramesh Nair - 1 May 2008
     * 
     * We need to track when do and don't have keyboard focus. This helps us 
     * determine when the text cursor should be shown.
     * 
     * See ticket #1356
     */ 
	public void focusGained( FocusEvent e )
    {
        dc.focusGained( e );
    }



    /**
     * BlueWhaleSystems fix: Ramesh Nair - 1 May 2008
     * 
     * We need to track when do and don't have keyboard focus. This helps us 
     * determine when the text cursor should be shown.
     * 
     * See ticket #1356
     */ 	
    public void focusLost( FocusEvent e )
    {
        dc.focusLost( e );
    }

}

